W600+STM32 AT指令(W600TCP服务器——用手机TCP调试助手发信息给单片机控制LED亮灭)

前言

好些天没有更新博客,是因为学习新知识去了,学习了如何用单片机发信息给W600模组,并利用W600的响应控制单片机上的LED的亮灭,下面把我学习的成果以及过程中遇到的一些问题记录下来,作为备忘,也分享给有需要的人。废话不多说,我们进入正题…
首先,我们需要梳理一下我们整个流程的思路:我们是通过单片机(STM32)的串口发送AT指令给wifi模块(这个是跟我们前面用串口调试助手是一个性质的);紧接着,我们同样需要利用串口接收wifi模块返回的响应信息,对响应的信息进行判断,做出正确的反应。
整个流程大致可以归纳为以上的那么一段话,接下来我们就根据具体代码来仔细讲解,代码不复杂,我写代码都是怎么简单怎么来,所以相信大家都可以看的懂。

代码讲解

串口初始化

用的是串口2收发信息,硬件上将串口2的TX,RX跟模组的RX,TX连接,下面贴上代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
u8 USART2_RX_BUF[USART2_REC_LEN];  //定义接收缓存数组(串口接受的数据存储在数组里)
u16 USART2_RX_STA=0;
int flag=0; //串口接收标志位

void W600_USART_init(void)
{
GPIO_InitTypeDef GPIO_InitStrue; //结构体定义
USART_InitTypeDef USART_InitStrue;
NVIC_InitTypeDef NVIC_InitStrue;

RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //使能串口2,GPIOA时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
//GPIOA初始化
GPIO_InitStrue.GPIO_Pin = GPIO_Pin_2; //USART2_TX PA.2
GPIO_InitStrue.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStrue.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出
GPIO_Init(GPIOA, &GPIO_InitStrue); //初始化PA2

GPIO_InitStrue.GPIO_Pin = GPIO_Pin_3; //USART2_RX PA.3
GPIO_InitStrue.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入
GPIO_Init(GPIOA, &GPIO_InitStrue); //初始化PA3

//USART初始化
USART_InitStrue.USART_BaudRate = 115200;//波特率
USART_InitStrue.USART_WordLength = USART_WordLength_8b;//字长为8位数据格式
USART_InitStrue.USART_StopBits = USART_StopBits_1;//一个停止位
USART_InitStrue.USART_Parity = USART_Parity_No;//无奇偶校验位
USART_InitStrue.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件数据流控制
USART_InitStrue.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //收发模式
USART_Init(USART2, &USART_InitStrue); //初始化串口

NVIC_InitStrue.NVIC_IRQChannel = USART2_IRQn;
NVIC_InitStrue.NVIC_IRQChannelPreemptionPriority=3 ;//抢占优先级3
NVIC_InitStrue.NVIC_IRQChannelSubPriority = 3; //子优先级3
NVIC_InitStrue.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStrue); //根据指定的参数初始化VIC寄存器
USART_ITConfig(USART2, USART_IT_RXNE, ENABLE);//开启中断

USART_Cmd(USART2,ENABLE); //使能串口
}

//*************************串口2中断服务函数*********************************//
void USART2_IRQHandler(void) //串口2中断服务程序
{
if(USART_GetITStatus(USART2, USART_IT_RXNE)!=RESET)
{
flag=1;
USART2_RX_BUF[USART2_RX_STA++]=USART_ReceiveData(USART2);
}
}

上面这段代码没什么特别的,就是串口的初始化,重点看一下中断服务函数,我们的信息接收是在中断中进行的,原本用的是正点原子的,但是却一直收不到数据,弄了很久才发现问题在于原子定义的协议,协议规定了接收到回车换行代表数据接收完成,但恰恰我用的wifi模块返回的信息是回车换行开头的……,所以我修改成没有任何协议,只要接收到数据就统统存储到前面定义的数组中。

WiFi模块初始化

说是模块的初始化,实际上模块没有什么要初始化的,硬件连接上去就可以收发数据,我们在这里做的就是编写单片机发送AT指令给模块配置AP模式的函数,直接贴代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//*******************预定义字符串*****************************//
char w600_rst[]="AT+RST\r\n"; //复位指令
char w600_at[]="AT\r\n"; //握手连接指令
char w600_cwmode1[]="AT+CWMODE=1\r\n"; //设置w600工作模式为STA
char w600_cwmode2[]="AT+CWMODE=2\r\n"; //设置w600工作模式为AP
char w600_cwjap[]="AT+CWJAP=\"USER_1A383A\",\"00000000\"\r\n"; //设置要加入的AP参数
char w600_cwsap[]="AT+CWSAP=\"lxz\",\"lxz333333\",5,3\r\n"; //设置要创建的AP参数
char w600_cipmux[]="AT+CIPMUX=1\r\n"; //使能多链接
char w600_cipserver[]="AT+CIPSERVER=1,6000\r\n"; //创建服务器
char w600_return[]="OK\r\n";

//*******************字符串发送函数*****************************//
void USART_SendString(USART_TypeDef* USARTx,char *str)
{
while(*str!='\0')
{
while(USART_GetFlagStatus(USARTx,USART_FLAG_TC )==RESET);
USART_SendData(USARTx,*str);
str++;
}
}

//*************************数组清空函数**************************//
void Data_Clear(void)
{
int i;
for(i=0;i<600;i++)
{
USART2_RX_BUF[i]=0;
}
}

//*************************返回信息比较函数************************//
Data_Compare(void)
{
int i;
int strx=0;
for(i=0;i<600;i++)
{
if((USART2_RX_BUF[i]=='O')&&(USART2_RX_BUF[i+1]=='K'))
{
strx=1;
}

return strx;
}
u8 Data_Compare1(void)
{
int i;
int strx=0;
for(i=0;i<600;i++)
{
if((USART2_RX_BUF[i]=='S')&&(USART2_RX_BUF[i+1]=='T'))
{
strx=1;
}
}
return strx;
}
u8 Data_Compare2(void)
{
int i;
int strx=0;
for(i=0;i<600;i++)
{
if((USART2_RX_BUF[i]=='O')&&(USART2_RX_BUF[i+1]=='N'))
{
strx=1;
}
}
return strx;
}

//***************************W600初始化**************************//
void W600_wifi_init(void)
{
USART_SendString(USART2, w600_at);
while(1){
if(Data_Compare()==1) break;
}
Data_Clear();
WLED1=1;
delay_ms(500);
USART_SendString(USART2, w600_cwmode2);
while(1){
if(Data_Compare()==1) break;
}
Data_Clear();
WLED1=0;
delay_ms(500);
USART_SendString(USART2, w600_cwsap);
while(1){
if(Data_Compare()==1) break;
}
Data_Clear();
WLED1=1;
delay_ms(500);
USART_SendString(USART2, w600_cipmux);
while(1){
if(Data_Compare()==1) break;
}
Data_Clear();
WLED1=0;
delay_ms(500);
USART_SendString(USART2, w600_cipserver);
while(1){
if(Data_Compare()==1) break;
}
Data_Clear();
WLED1=1;
}

上面这段代码有几个函数我们讲解一下,首先是数组清空函数void Data_Clear(void),这个函数的作用是清空每一次我们接收的内容,为了下一次接收新的数据做准备,避免出错;还有一个就是返回信息比较函数Data_Compare(void),因为我们每一次发送AT指令,模块都会返回响应信息,我们需要判断是否接收到正确的信息来确定是否发送新的AT指令;最后一个就是系列指令发送的函数了void W600_wifi_init(void),这个函数我们通过系列指令来配置AP模式,使能多链接以及确定端口号…每发送一个指令,判断一次返回信息,这里还用LED亮灭来辅助观察,相信大家看代码是可以看的懂的。

主函数

下面我们看看主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
int main(void)
{
int i;
delay_init(); //延时函数初始化
NVIC_Configuration(); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
uart_init(9600); //串口初始化为9600
KEY_Init();
LED_Init();
W600_USART_init();
W600_wifi_init();
while(1)
{
if(Data_Compare1()==1) break;
}
Data_Clear();
while(1)
{
if(Data_Compare2()==1) break;
}
Data_Clear();
flag =0;

while(1)
{
if(flag == 1){
flag =0;
for(i=0;i<600;i++){
if(USART2_RX_BUF[i]=='1' && USART2_RX_BUF[i+1]==':')
{
switch(USART2_RX_BUF[i+2])
{
case 'O': WLED2=1;
break;
case 'F': WLED2=0;
break;
default: break;
}
}
}
}
}
}

这里头还用到了两个返回信息比较函数,因为我们手机连上热点和连接上对应端口,模块都会分别返回信息到串口,这里也要判断一下;最后我们再次讲解一下我们的串口中断服务函数,里头定义了每次接收到数据,标志位置1,前面我们不讲是因为我们没用到这个标志位,当我们手机TCP调试助手连接上模块后,我们的准备工作就算是做完了,这时将标志位置0,接下来就是等待发送亮灭等的信息了,这里我们定义循环接收信息,当发送亮灭等信号后串口接收到新的数据(也就是当标志位被置1),我们判断是要亮还是灭,然后做出正确响应。(亮时O,灭时F,这些都是我们自己可以定义修改的)。
好了,代码这样就讲解完了,大家实践试试吧,本片完结!!!